﻿using System;
using System.ComponentModel;
using System.Drawing;
using System.Xml.Serialization;
using Microsoft.WowAddonStudio.FrameXml.Serialization.Attributes;
using Microsoft.WowAddonStudio.FrameXml.Serialization.Enumerations;
using Microsoft.WowAddonStudio.FrameXml.Serialization.TypeConverters;

namespace Microsoft.WowAddonStudio.FrameXml.Serialization.LayoutFrames
{
    [Serializable]
    [DesignerCategory("code")]
    [XmlType(Namespace = "http://www.blizzard.com/wow/ui/")]
    [XmlRoot(Namespace = "http://www.blizzard.com/wow/ui/", IsNullable = false)]
    [TypeConverter(typeof(EmptyExpandableObjectConverter))]
    public class FontString : LayeredLayoutFrame, ICloneable, ICopiable<FontString>
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="FontString"/> class.
        /// </summary>
        public FontString()
            : base(DrawLayer.Overlay)
        {
        }

        /// <summary>
        /// Gets or sets the font used to draw the fontstring.
        /// </summary>
        [XmlAttribute("font")]
        [File(FileType.Font)]
        [Category("Appearance")]
        [Description("The font used to display the text.")]
        public string Font
        {
            get { return Properties.GetValue<string>("Font"); }
            set { Properties.SetValue("Font", value); }
        }

        /// <summary>
        /// Gets or sets the number of bytes allocated for the fontstring's buffer.
        /// </summary>
        [XmlAttribute("bytes")]
        [DefaultValue(255)]
        [Category("Appearance")]
        [Description("The number of bytes allocated for the fontstring's buffer.")]
        public int Bytes
        {
            get { return Properties.GetValue<int>("Bytes"); }
            set { Properties.SetValue("Bytes", value); }
        }

        /// <summary>
        /// Gets or sets the text of the fontstring.
        /// </summary>
        [XmlAttribute("text")]
        [Category("Behavior")]
        [Description("Text to display on the control.")]
        public string Text
        {
            get { return Properties.GetValue<string>("Text"); }
            set { Properties.SetValue("Text", value); }
        }

        /// <summary>
        /// Gets or sets the spacing between the charactes of the fontstring.
        /// </summary>
        [XmlAttribute("spacing")]
        [DefaultValue(0.0f)]
        [Category("Appearance")]
        [Description("The spacing between the charactes of the fontstring.")]
        public float Spacing
        {
            get { return Properties.GetValue<float>("Spacing"); }
            set { Properties.SetValue("Spacing", value); }
        }

        /// <summary>
        /// Gets or sets the outline of the fontstring.
        /// </summary>
        [XmlAttribute("outline")]
        [DefaultValue(Outline.None)]
        [Category("Appearance")]
        [Description("The outline of the fontstring.")]
        public Outline Outline
        {
            get { return Properties.GetValue<Outline>("Outline"); }
            set { Properties.SetValue("Outline", value); }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the fontstring is monochrome.
        /// </summary>
        [XmlAttribute("monochrome")]
        [DefaultValue(false)]
        [Category("Appearance")]
        [Description("The value indicating whether the fontstring is monochrome.")]
        public bool Monochrome
        {
            get { return Properties.GetValue<bool>("Monochrome"); }
            set { Properties.SetValue("Monochrome", value); }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the fontstring should be wrapped at non-space characters.
        /// </summary>
        [XmlAttribute("nonspacewrap")]
        [DefaultValue(false)]
        [Category("Behavior")]
        [Description("Don't wrap text when it gets to the end of the parent frame.")]
        public bool NonSpaceWrap
        {
            get { return Properties.GetValue<bool>("NonSpaceWrap"); }
            set { Properties.SetValue("NonSpaceWrap", value); }
        }

        /// <summary>
        /// Gets or sets the vertical justification of the fontstring. The default is middle.
        /// </summary>
        [XmlAttribute("justifyV")]
        [DefaultValue(JustifyVertical.Middle)]
        [Category("Appearance")]
        [Description("Align text \"TOP\", \"BOTTOM\", or \"MIDDLE\". The default is middle.")]
        public JustifyVertical JustifyVertical
        {
            get { return Properties.GetValue<JustifyVertical>("JustifyVertical"); }
            set { Properties.SetValue("JustifyVertical", value); }
        }

        /// <summary>
        /// Gets or sets the horizontal justification of the fontstring. The default is center.
        /// </summary>
        [XmlAttribute("justifyH")]
        [DefaultValue(JustifyHorizontal.Center)]
        [Category("Appearance")]
        [Description("Align text \"LEFT\", \"RIGHT\", or \"CENTER\". The default is center.")]
        public JustifyHorizontal JustifyHorizontal
        {
            get { return Properties.GetValue<JustifyHorizontal>("JustifyHorizontal"); }
            set { Properties.SetValue("JustifyHorizontal", value); }
        }

        /// <summary>
        /// Gets or sets the number of maximum lines for the fontstring. The default is unlimited (0).
        /// </summary>
        [XmlAttribute("maxLines")]
        [DefaultValue(typeof (uint), "0")]
        [Category("Appearance")]
        [Description("Defines the maximum number of lines to allow in a text box. The default is unlimited (0).")]
        public uint MaxLines
        {
            get { return Properties.GetValue<uint>("MaxLines"); }
            set { Properties.SetValue("MaxLines", value); }
        }

        [XmlElement]
        [Browsable(true)]
        [Category("Appearance")]
        [Description("The FontHeight of the fontstring.")]
        public FontHeight FontHeight
        {
            get { return Properties.GetValue<FontHeight>("FontHeight"); }
            set { Properties.SetValue("FontHeight", value); }
        }

        /// <summary>
        /// Gets or sets the <see cref="Color"/> of the fontstring.
        /// </summary>
        [XmlElement]
        [Color]
        [EditorBrowsable]
        [Category("Appearance")]
        [Description("The color used to display text.")]
        public Color Color
        {
            get { return Properties.GetValue<Color>("Color"); }
            set { Properties.SetValue("Color", value); }
        }

        /// <summary>
        /// Gets or sets the <see cref="Shadow"/> of the fontstring.
        /// </summary>
        [XmlElement]
        [Shadow]
        [EditorBrowsable]
        [Category("Appearance")]
        [NotifyParentProperty(true)]
        [Description("Puts a shadow behind an element.")]
        public Shadow Shadow
        {
            get { return Properties.GetValue<Shadow>("Shadow"); }
            set { Properties.SetValue("Shadow", value); }
        }

        /// <summary>
        /// Gets the vertical and horizontal alignment of the fontstring as a <see cref="StringFormat"/>.
        /// </summary>
        [XmlIgnore]
        [Category("Appearance")]
        [Browsable(false)]
        public StringFormat StringFormat
        {
            get
            {
                var stringFormat = new StringFormat();

                switch (JustifyVertical)
                {
                    case JustifyVertical.Top:
                        stringFormat.LineAlignment = StringAlignment.Near;
                        break;
                    case JustifyVertical.Middle:
                        stringFormat.LineAlignment = StringAlignment.Center;
                        break;
                    case JustifyVertical.Bottom:
                        stringFormat.LineAlignment = StringAlignment.Far;
                        break;
                }

                switch (JustifyHorizontal)
                {
                    case JustifyHorizontal.Left:
                        stringFormat.Alignment = StringAlignment.Near;
                        break;
                    case JustifyHorizontal.Center:
                        stringFormat.Alignment = StringAlignment.Center;
                        break;
                    case JustifyHorizontal.Right:
                        stringFormat.Alignment = StringAlignment.Far;
                        break;
                }

                return stringFormat;
            }
        }

        /// <summary>
        /// Gets a value indicating whether the fontstring is complete.
        /// </summary>
        [XmlIgnore]
        public override bool IsComplete
        {
            get
            {
                return !String.IsNullOrEmpty(Font) &&
                       !String.IsNullOrEmpty(Text) &&
                       FontHeight != null && FontHeight.IsComplete &&
                       Color != null;
            }
        }

        #region ICloneable Members

        /// <summary>
        /// Creates a new object that is a copy of the current instance.
        /// </summary>
        /// <returns>
        /// A new object that is a copy of this instance.
        /// </returns>
        public new object Clone()
        {
            return Copy();
        }

        #endregion

        #region ICopiable<FontString> Members

        /// <summary>
        /// Creates a new object that is a copy of the instance.
        /// </summary>
        /// <returns>The copied instance.</returns>
        /// <remarks>Performs a deep copy.</remarks>
        public new FontString Copy()
        {
            return new FontString
                       {
                           Bytes = Bytes,
                           Color = Color != null ? Color.Copy() : null,
                           DrawLayer = DrawLayer,
                           Font = Font,
                           FontHeight = FontHeight != null ? FontHeight.Copy() : null,
                           Hidden = Hidden,
                           InheritanceEnabled = InheritanceEnabled,
                           InheritedObject = InheritedObject,
                           Inherits = Inherits,
                           JustifyHorizontal = JustifyHorizontal,
                           JustifyVertical = JustifyVertical,
                           LayoutHidden = LayoutHidden,
                           MaxLines = MaxLines,
                           Monochrome = Monochrome,
                           Name = Name,
                           NonSpaceWrap = NonSpaceWrap,
                           Outline = Outline,
                           ParentFrame = ParentFrame,
                           SetAllPoints = SetAllPoints,
                           Shadow = Shadow != null ? Shadow.Copy() : null,
                           SizeDimension = SizeDimension != null ? SizeDimension.Copy() : null,
                           Spacing = Spacing,
                           Text = Text,
                           Virtual = Virtual
                       };
        }

        #endregion

        /// <summary>
        /// Equalses the specified other.
        /// </summary>
        /// <param name="other">The other.</param>
        /// <returns></returns>
        public bool Equals(FontString other)
        {
            if (other == this)
                return true;
            if (other == null)
                return false;

            return Equals(Bytes, other.Bytes) &&
                   Equals(Color, other.Color) &&
                   Equals(DrawLayer, other.DrawLayer) &&
                   Equals(Font, other.Font) &&
                   Equals(FontHeight, other.FontHeight) &&
                   Equals(Hidden, other.Hidden) &&
                   Equals(InheritanceEnabled, other.InheritanceEnabled) &&
                   Equals(InheritedObject, other.InheritedObject) &&
                   Equals(Inherits, other.Inherits) &&
                   Equals(JustifyHorizontal, other.JustifyHorizontal) &&
                   Equals(JustifyVertical, other.JustifyVertical) &&
                   Equals(LayoutHidden, other.LayoutHidden) &&
                   Equals(MaxLines, other.MaxLines) &&
                   Equals(Monochrome, other.Monochrome) &&
                   Equals(Name, other.Name) &&
                   Equals(NonSpaceWrap, other.NonSpaceWrap) &&
                   Equals(Outline, other.Outline) &&
                   Equals(ParentFrame, other.ParentFrame) &&
                   Equals(SetAllPoints, other.SetAllPoints) &&
                   Equals(Shadow, other.Shadow) &&
                   Equals(SizeDimension, other.SizeDimension) &&
                   Equals(Spacing, other.Spacing) &&
                   Equals(Text, other.Text) &&
                   Equals(Virtual, other.Virtual);
        }

        /// <summary>
        /// Determines whether the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>.
        /// </summary>
        /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param>
        /// <returns>
        /// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
        /// </returns>
        /// <exception cref="T:System.NullReferenceException">
        /// The <paramref name="obj"/> parameter is null.
        /// </exception>
        public override bool Equals(object obj)
        {
            return (Equals(obj as FontString));
        }

        /// <summary>
        /// Serves as a hash function for a particular type.
        /// </summary>
        /// <returns>
        /// A hash code for the current <see cref="T:System.Object"/>.
        /// </returns>
        public override int GetHashCode()
        {
            return Name != null ? Name.GetHashCode() : 0;
        }
    }
}